home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / CLASSSRC.PAK / LOCALE.CPP < prev    next >
C/C++ Source or Header  |  1997-05-06  |  11KB  |  321 lines

  1. //----------------------------------------------------------------------------
  2. // Borland WinSys Library
  3. // Copyright (c) 1994, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   5.9  $
  6. //
  7. // TLocaleString implementation - localized name support
  8. // TRegList implementation - runtime component and object registration list
  9. // TRegItem implementation - runtime component and object registration item
  10. //
  11. // NOTE: This code must reside in the same module that the strings are defined
  12. //       The cache, NativeLangId, HINSTANCE are managed on a per-module basis
  13. //       TLocaleString::NativeLangId may be user-implemented to symbol langid
  14. //       TLocaleString::Module may be reset from this to another resource DLL
  15. //----------------------------------------------------------------------------
  16. #include <winsys/pch.h>
  17. #include <winsys/lclstrng.h>
  18. #include <winsys/wsysinc.h>  // just for _hInstance
  19. #include <stdio.h>
  20.  
  21. //----------------------------------------------------------------------------
  22. // Module global default values - except for TLocaleString::NativeLangId
  23. //
  24. TLangId TLocaleString::SystemDefaultLangId = TLocaleString::GetSystemLangId();
  25. TLangId TLocaleString::UserDefaultLangId   = TLocaleString::GetUserLangId();
  26. HINSTANCE TLocaleString::Module = _hInstance;
  27. TLocaleString TLocaleString::Null = {""};
  28.  
  29. //----------------------------------------------------------------------------
  30. // TLocaleCache definitions, private for implementation
  31. //
  32.  
  33. #define AUTOLANG_CACHEDNEUT 0x02 // prefix indicates cache entry with neutral
  34. #define AUTOLANG_CACHEDLOAD 0x01 // prefix indicates Neutral is not a string
  35. const TLangId InvalidLangId = 0xFFFF;
  36.  
  37. struct TLocaleCache;
  38. struct TLocaleCacheBase;
  39.  
  40. //
  41. // Static object to hold destructable pointer
  42. //
  43. struct TLocaleCacheList {
  44.   TLocaleCacheList() : Next(0) {}
  45.  ~TLocaleCacheList();                     // releases cache entries
  46.   TLocaleCache* Lookup(const _TCHAR* name); // returns cache entry, 0 if failed
  47.   TLocaleCacheBase* Next;                 // linked list of cached translations
  48. };
  49.  
  50. //
  51. // This base struct is used to cache failure to find language resource
  52. //
  53. struct TLocaleCacheBase {
  54.   long  Hash;            // hashed original string, for duplicate elimination
  55.   const _TCHAR* Neutral;   // original string, +1 if resource found and loaded
  56.   TLocaleCacheBase* Next;// linked list of cached strings, for search, cleanup
  57.  
  58.   TLocaleCacheBase(const _TCHAR* name, long hash);
  59.   static TLocaleCacheList CacheList;
  60. };
  61.  
  62. //
  63. // Buffer follows this header, sized for maximum string + null term.
  64. //
  65. struct TLocaleCache : public TLocaleCacheBase {
  66.   void* operator new(unsigned size, unsigned buflen);
  67.   TLocaleCache(const _TCHAR* name, long hash, HRSRC rscHdl, HGLOBAL resData);
  68.  ~TLocaleCache() {}
  69.   const _TCHAR* Translate(TLangId lang);   // (re)translate string
  70.  
  71.   TLangId ActLangId;   // actual language ID of cached string
  72.   TLangId ReqLangId;   // requested language ID of cached string
  73.   HRSRC   ResHdl;      // handle returned from ::FindResource()
  74.   _TCHAR    Buf[1];      // first character is string type
  75. };
  76.  
  77. //----------------------------------------------------------------------------
  78. // TLocaleCache implementation
  79. //
  80.  
  81. TLocaleCacheBase::TLocaleCacheBase(const _TCHAR* name, long hash)
  82. :
  83.   Neutral(name),
  84.   Hash(hash)
  85. {
  86.   Next = CacheList.Next;
  87.   CacheList.Next = this;
  88. }
  89.  
  90. void* TLocaleCache::operator new(unsigned size, unsigned buflen)
  91. {
  92.   return ::operator new(size+buflen);
  93. }
  94.  
  95. TLocaleCache::TLocaleCache(const _TCHAR* name, long hash,
  96.                            HRSRC resHdl, HGLOBAL resData)
  97. :
  98.   TLocaleCacheBase(name, hash),
  99.   ResHdl(resHdl)
  100. {
  101.   ReqLangId = ActLangId = InvalidLangId;     // indicate initializing state
  102.   *(HGLOBAL*)(Buf+1) = resData;   // store resource pointer temp in buffer
  103. }
  104.  
  105. TLocaleCache* TLocaleCacheList::Lookup(const _TCHAR* name)
  106. {
  107.   const _TCHAR* neut = name + 1;      // skip over prefix flag char
  108.   long hash = 0;
  109.   const _TCHAR* pc = name;
  110.   while (*pc)
  111.     hash = hash*2 ^ *pc++;
  112.   for (TLocaleCacheBase* entry = Next; entry; entry = entry->Next) {
  113.     if (hash == entry->Hash) {
  114.       const _TCHAR* pc = entry->Neutral;
  115.       if (*pc != *neut)       // Neutral points to prefix if lookup failed
  116.         pc++;
  117.       if (TLocaleString::CompareLang(pc,neut,TLocaleString::NativeLangId) != 0)
  118.         return  pc == entry->Neutral ? (TLocaleCache*)entry : 0;
  119.     }
  120.   }
  121.   pc = name;
  122.   if (*name != AUTOLANG_RCID)
  123.     pc++;                    // '#' part of Id
  124.   HRSRC resHdl = ::FindResource(TLocaleString::Module, pc, RT_LOCALIZATION);
  125.   if (!resHdl) {
  126.     new TLocaleCacheBase(name, hash);    // add cache entry for failed lookup
  127.     return 0;
  128.   }
  129.   HGLOBAL resData = ::LoadResource(TLocaleString::Module, resHdl);
  130.   if (!resData) {
  131.     return 0;     // should throw exception on failure?!!
  132.   }
  133.   _TUCHAR far* pr = (_TUCHAR far*)::LockResource(resData);
  134.   int maxLen = sizeof(HGLOBAL);  // scan for longest string, including null
  135.   _TUCHAR c = *pr;         // check first byte of langid or neutral text
  136.   if (c == 0) {                  // check for empty resource string
  137.     ::FreeResource(resData);
  138.     new TLocaleCacheBase(name, hash); // add failed cache entry if null or err
  139.     return 0;
  140.   }
  141.   if (c >= ' ')                  // check for unprefixed neutral string first
  142.     pr--;                        // cancel ++ in for loop initialization
  143.   else
  144.     pr++;                        // start to skip over 2-byte language id
  145.   do {                           // loop to check for maximum string length
  146.     _TUCHAR far* p = ++pr; // skip over id to start of translation
  147.     while ((c=*pr++) >= ' ') ;   // skip over translation string
  148.     if ((int)(pr-p) > maxLen)    // update maximum, including terminator
  149.       maxLen = (int)(pr-p);
  150.   } while(c);
  151.   TLocaleCache* cache = new(maxLen) TLocaleCache(neut, hash, resHdl, resData);
  152.   cache->Buf[0] = _TCHAR(*name == AUTOLANG_XLAT ? AUTOLANG_CACHEDNEUT
  153.                                               : AUTOLANG_CACHEDLOAD);
  154.   return cache;
  155. }
  156.  
  157. const _TCHAR* TLocaleCache::Translate(TLangId reqLang)
  158. {
  159.   HGLOBAL resData;
  160.   if (ReqLangId == InvalidLangId) { // if first time called after construction
  161.     resData = *(HGLOBAL*)(Buf+1);
  162.     ReqLangId = reqLang;
  163.   }
  164.   else {
  165.     if (Buf[0]==AUTOLANG_CACHEDNEUT && TLocaleString::IsNativeLangId(reqLang))
  166.       return Neutral;
  167.     if (reqLang == ActLangId)
  168.       return Buf+1;
  169.     if (reqLang == ReqLangId) {
  170.       if (ActLangId != InvalidLangId)
  171.         return Buf+1;
  172.       else if (Buf[0] == AUTOLANG_CACHEDNEUT)
  173.         return Neutral;
  174.       else
  175.         return 0;
  176.     }
  177.     if ((resData = ::LoadResource(TLocaleString::Module, ResHdl)) == 0)
  178.       return Neutral;   // should throw exception on failure?!!
  179.   }
  180.  
  181.   _TUCHAR far* resBuf = (_TUCHAR far*)::LockResource(resData);
  182.   _TUCHAR far* translation = 0;
  183.   _TUCHAR far* pr = resBuf;
  184.   TLangId            actLang = InvalidLangId;
  185.   TLangId            resLang;
  186.   _TUCHAR c;
  187.  
  188.   while ((c = *pr) != 0) {
  189.     if (c > ' ') { // check for initial neutral string, used with CACHEDLOAD
  190.       actLang = resLang = TLocaleString::NativeLangId;
  191.       translation = pr;    // lowest preference match
  192.     }
  193.     else {
  194.       resLang = TLangId(((c - 1)<<10) | *++pr);
  195.       pr++;
  196.     }
  197.     if (resLang == reqLang) {     // exact match
  198.       translation = pr;
  199.       actLang = resLang;
  200.       break;
  201.     }
  202.     if ((_TCHAR)resLang == (_TCHAR)reqLang) {   // base language match
  203.       if ((_TCHAR)actLang != (_TCHAR)reqLang || resLang == (reqLang & 0x00FF)) {
  204.         translation = pr;
  205.         actLang = resLang;
  206.       }
  207.     }
  208.     for ( ; *pr >= ' '; ++pr)   // skip over translation string till next Id
  209.       ;
  210.   }
  211.   const _TCHAR* retVal;
  212.   if (translation) {
  213.     while (*translation < ' ')    // skip over multiple language IDs
  214.       translation += 2;
  215.     if (actLang != ActLangId) {   // if same as in buffer, leave alone
  216.       _TCHAR* pc;
  217.       for (pr = translation, pc = Buf + 1; *pr >= ' '; )
  218.         *pc++ = *pr++;
  219.       *pc = 0;
  220.       ActLangId = actLang;
  221.       if (reqLang != ActLangId)
  222.         ReqLangId = reqLang;
  223.     }
  224.     retVal = Buf+1;
  225.   }
  226.   else if (Buf[0] == AUTOLANG_CACHEDNEUT) {
  227.     retVal = Neutral;
  228.   }
  229.   else {
  230.     retVal = 0;
  231.   }
  232.   ::FreeResource(resData);
  233.   return retVal;
  234. }
  235.  
  236. TLocaleCacheList TLocaleCacheBase::CacheList;  // declare module-global cache
  237.  
  238. TLocaleCacheList::~TLocaleCacheList()
  239. {
  240.   while (Next) {
  241.     TLocaleCacheBase* p = Next;
  242.     Next = Next->Next;
  243.     delete p;
  244.   }
  245. }
  246.  
  247. //----------------------------------------------------------------------------
  248. // TLocaleString implementation, except for static int CompareLang(s1,s2,lang)
  249. //
  250.  
  251. const _TCHAR* TLocaleString::Translate(TLangId reqLang)
  252. {
  253.   if (!Private)                   // check for null string pointer
  254.     return Private;
  255.  
  256.   if (reqLang == LangNeutral)
  257.     reqLang = NativeLangId;
  258.   else if (reqLang == LangSysDefault)
  259.     reqLang = SystemDefaultLangId;
  260.   else if (reqLang == LangUserDefault)
  261.     reqLang = UserDefaultLangId;
  262.  
  263.   TLocaleCache* cache;
  264.   switch (Private[0])
  265.   {
  266.     default:                      // untranslatable string, no prefix
  267.       return Private;
  268.  
  269.     case AUTOLANG_XLAT:           // not yet translated
  270.       if (IsNativeLangId(reqLang))
  271.         return Private+1;         // resource name IS neutral or default name
  272.       if ((cache = TLocaleCacheBase::CacheList.Lookup(Private)) == 0)
  273.         return ++Private;         // permanently bump pointer to make constant
  274.       Private = cache->Buf;       // point to buffer in cache
  275.       return cache->Translate(reqLang);
  276.  
  277.     case AUTOLANG_LOAD:           // named resource not accessed yet
  278.     case AUTOLANG_RCID:           // numeric resource not accessed yet
  279.       if ((cache = TLocaleCacheBase::CacheList.Lookup(Private)) == 0)
  280.         return (Private = 0);     // permanently set pointer to null
  281.       Private = cache->Buf;       // point to buffer in cache
  282.       return cache->Translate(reqLang);
  283.  
  284.     case AUTOLANG_CACHEDNEUT:     // string in cache with neutral pointer
  285.     case AUTOLANG_CACHEDLOAD:     // string in cache with no neutral pointer
  286.       cache = (TLocaleCache*)(Private+1) - 1;   // backup to point to header
  287.       return cache->Translate(reqLang);
  288.   }
  289. }
  290.  
  291. TLocaleString::operator const _TCHAR*()
  292. {
  293.   if (Private == 0)
  294.     return 0;
  295.  
  296.   switch (Private[0]) {
  297.   case AUTOLANG_XLAT:       // not yet translated
  298.   case AUTOLANG_CACHEDNEUT: // translated string in cache
  299.   case AUTOLANG_CACHEDLOAD: // translated or neutral name in cache
  300.     return Private+1;
  301.  
  302.   case AUTOLANG_RCID:       // resource not accessed yet
  303.   case AUTOLANG_LOAD:       // resource not accessed yet
  304.     return 0;
  305.  
  306.   default:                  // untranslatable string, no prefix
  307.     return Private;
  308.   }
  309. }
  310.  
  311. int TLocaleString::Compare(const _TCHAR far* str, TLangId lang)
  312. {
  313.   return CompareLang(this->Translate(lang), str, lang);
  314. }
  315.  
  316. int TLocaleString::IsNativeLangId(TLangId lang)
  317. {
  318.   return lang == NativeLangId || lang == (NativeLangId & 0x00FF);
  319. }
  320.  
  321.